package com.xpalive.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Deque;

public class PercentValue {

    //需要计算百分比的数据
    private final BigDecimal[] arr;
    //数据数量
    private final int valLen;
    //精度
    private final int precision;
    //增强后的精度
    private final int precisionPow;
    //增强的幅度
    private final int precisionPowInCrease = 6;
    //数据总和
    private final BigDecimal sum;

    private final BigDecimal ONE_HUNDRED = new BigDecimal("100");
    private static final BigDecimal ZERO = BigDecimal.ZERO;
    private static final BigDecimal TEN = BigDecimal.TEN;
    private static final BigDecimal ONE = BigDecimal.ONE;

    //结果
    private final BigDecimal[] result;

    public static PercentValue of(int[] vals, int precision) {
        BigDecimal[] bigDecimals = Arrays.stream(vals).mapToObj(BigDecimal::new)
            .toArray(BigDecimal[]::new);
        return new PercentValue(bigDecimals, precision);
    }

    public PercentValue(BigDecimal[] arr, int precision) {
        this.arr = arr;
        this.valLen = arr.length;
        this.precision = precision;
        this.precisionPow = precision + precisionPowInCrease;
        this.sum = sum(arr);
        this.result = getPercentValue();
    }

    private BigDecimal sum(BigDecimal[] bigDecimals) {
        BigDecimal sum = ZERO;
        for (int i = 0; i < valLen; i++) {
            sum = sum.add(bigDecimals[i]);
        }
        return sum;
    }

    private BigDecimal[] getPercentValue() {
        BigDecimal[] precisionPowArr = new BigDecimal[valLen];
        BigDecimal[] precisionArr = new BigDecimal[valLen];
        BigDecimal[] precisionRemainder = new BigDecimal[valLen];
        //计算当前实际百分比的值
        BigDecimal actualPerSum = ZERO;
        for (int i = 0; i < valLen; i++) {
            //值 * 100 获取百分比 100%
            BigDecimal val = this.arr[i].multiply(ONE_HUNDRED);
            //增强的精度
            precisionPowArr[i] = val.divide(sum, precisionPow, RoundingMode.HALF_UP);
            //指定的精度 忽略指定精度后的小数位
            precisionArr[i] = val.divide(sum, precision, RoundingMode.DOWN);
            //增强精度 - 指定精度的值的剩余精度
            precisionRemainder[i] = precisionPowArr[i].subtract(precisionArr[i]);
            //计算当前实际百分比的值
            actualPerSum = actualPerSum.add(precisionArr[i]);
        }

        //当前精度不达100时需要根据剩余精度的大小顺序累加精度至100
        if (actualPerSum.compareTo(ONE_HUNDRED) < 0) {
            BigDecimal increase = ONE.divide(TEN.pow(precision), precision, RoundingMode.DOWN);
            //排序精度顺序
            SortArrIndex sortArrIndex = new SortArrIndex(precisionRemainder);
            //获取下标
            Deque<Integer> indexQue = sortArrIndex.getIndexQue();
            while (actualPerSum.compareTo(ONE_HUNDRED) < 0) {
                Integer index = indexQue.pollFirst();
                //根据下标顺序增加精度
                precisionArr[index] = precisionArr[index].add(increase);
                actualPerSum = actualPerSum.add(increase);
            }
        }
        return precisionArr;
    }

    public BigDecimal[] getResult() {
        return result;
    }
}
